package com.knowswift.security;

import com.knowswift.security.account.UserService;
import com.knowswift.security.configurer.JwtAuthorizeConfigurer;
import com.knowswift.security.configurer.LoginConfigurer;
import com.knowswift.security.filter.*;
import com.knowswift.security.handler.fail.CodeLoginFailureHandler;
import com.knowswift.security.handler.fail.LoginFailureHandler;
import com.knowswift.security.handler.fail.RestAuthenticationAccessDeniedHandler;
import com.knowswift.security.handler.fail.TokenAuthenticationFailureHandler;
import com.knowswift.security.handler.success.*;
import com.knowswift.security.log.LoginLogService;
import com.knowswift.security.provider.*;
import com.knowswift.security.token.*;
import com.knowswift.security.util.RedisUtils;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.header.Header;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * @company https://www.knowswift.com
 */
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    @Resource
    private RedisUtils redisUtils;

//    @Resource
//    private AdminService adminService;

    @Resource
    private UserService userService;

    @Resource
    private RestAuthenticationAccessDeniedHandler restAuthenticationAccessDeniedHandler;

    @Resource
    private TokenAuthenticationFailureHandler tokenAuthenticationFailureHandler;
    /**
     * Handler
     */

    @Resource
    private LoginFailureHandler loginFailureHandler;

    @Resource
    private CodeLoginFailureHandler codeLoginFailureHandler;

    @Resource
    private AdminPasswordLoginSuccessHandler adminPasswordLoginSuccessHandler;

    @Resource
    private UserCodeLoginSuccessHandler userCodeLoginSuccessHandler;

    @Resource
    private UserMiniProgramLoginSuccessHandler userMiniProgramLoginSuccessHandler;

    @Resource
    private UserPasswordLoginSuccessHandler userPasswordLoginSuccessHandler;

    @Resource
    private UserWeChatLoginSuccessHandler userWeChatLoginSuccessHandler;

    @Resource
    private LoginLogService loginLogService;

    @Resource
    private UserAppleLoginSuccessHandler userAppleLoginSuccessHandler;

    /**
     * Provider
     * 配置各种登录方法的provider
     */

//    protected JwtAuthenticationProvider adminJwtAuthenticationProvider() {
//        return new JwtAuthenticationProvider(adminService, AdminJwtAuthenticationToken.class, "tokenVersion");
//    }
    protected JwtAuthenticationProvider userJwtAuthenticationProvider() {
        return new JwtAuthenticationProvider(userService, UserJwtAuthenticationToken.class);
    }

//    public AuthenticationProvider adminAuthentication() {
//        DaoAuthenticationProvider provider = new AdminAuthenticationProvider();
//        provider.setUserDetailsService(adminService);
//        provider.setHideUserNotFoundExceptions(false);
//        provider.setPasswordEncoder(bCryptPasswordEncoder);
//        return provider;
//    }

    public AuthenticationProvider userAuthentication() {
        DaoAuthenticationProvider provider = new UserAuthenticationProvider();
        provider.setUserDetailsService(userService);
        provider.setHideUserNotFoundExceptions(false);
        provider.setPasswordEncoder(bCryptPasswordEncoder);
        return provider;
    }

    protected VerifyCodeAuthenticationProvider userCodeAuthenticationProvider() {
        return new VerifyCodeAuthenticationProvider(userService, UserVerifyCodeAuthenticationToken.class, redisUtils);
    }

    protected MiniProgramAuthenticationProvider userMiniProgramAuthenticationProvider() {
        return new MiniProgramAuthenticationProvider(userService, UserMiniProgramAuthenticationToken.class, loginLogService);
    }

    protected WeChatAuthenticationProvider userWeChatAuthenticationProvider() {
        return new WeChatAuthenticationProvider(userService, UserWeChatAuthenticationToken.class);
    }

    protected AppleAuthenticationProvider appleAuthenticationProvider() {
        return new AppleAuthenticationProvider(userService, AppleAuthenticationToken.class, loginLogService);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth
//                .authenticationProvider(adminAuthentication())
//                .authenticationProvider(adminJwtAuthenticationProvider())
                .authenticationProvider(userJwtAuthenticationProvider())
                .authenticationProvider(userCodeAuthenticationProvider())
                .authenticationProvider(userMiniProgramAuthenticationProvider())
                .authenticationProvider(userAuthentication())
                .authenticationProvider(userWeChatAuthenticationProvider())
                .authenticationProvider(appleAuthenticationProvider())
        ;


    }

    /**
     * Filter
     */
    protected JwtAuthenticationFilter configJwtAuthenticationFilter(String path, Class<? extends JwtAuthenticationToken> authClass,
                                                                    AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                    AuthenticationFailureHandler authenticationFailureHandler, String... excludeUrl) {
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter(path, authClass);
        filter.setPermissiveUrl(excludeUrl);
        filter.setSuccessHandler(authenticationSuccessHandler);
        filter.setFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected AbstractAuthenticationProcessingFilter configCodeLoginFilter(String path, Class<? extends VerifyCodeAuthenticationToken> authClass,
                                                                           AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                           AuthenticationFailureHandler authenticationFailureHandler) {
        VerifyCodeAuthenticationFilter filter = new VerifyCodeAuthenticationFilter(path, authClass);
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected PasswordAuthenticationFilter configPasswordLoginFilter(String path, Class<? extends UsernamePasswordAuthenticationToken> authClass,
                                                                     AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                     AuthenticationFailureHandler authenticationFailureHandler) {
        PasswordAuthenticationFilter filter = new PasswordAuthenticationFilter(path, authClass);
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected MiniProgramAuthenticationFilter configMiniProgramLoginFilter(String path, Class<? extends MiniProgramAuthenticationToken> authClass,
                                                                           AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                           AuthenticationFailureHandler authenticationFailureHandler) {
        MiniProgramAuthenticationFilter filter = new MiniProgramAuthenticationFilter(path, authClass);
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected WeChatAuthenticationFilter configWeChatLoginFilter(String loginPath, Class<? extends WeChatAuthenticationToken> authClass,
                                                                 AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                 AuthenticationFailureHandler authenticationFailureHandler) {
        WeChatAuthenticationFilter filter = new WeChatAuthenticationFilter(loginPath, authClass);
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected AppleAuthenticationFilter configAppleLoginFilter(String loginPath, Class<? extends AppleAuthenticationToken> authClass,
                                                               AuthenticationSuccessHandler authenticationSuccessHandler,
                                                               AuthenticationFailureHandler authenticationFailureHandler) {
        AppleAuthenticationFilter filter = new AppleAuthenticationFilter(loginPath, authClass);
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailureHandler);
        return filter;
    }

    protected WebSocketRequestFilter configWebSocketRequestFilter(String path, Class<? extends JwtAuthenticationToken> authClass,
                                                                  AuthenticationSuccessHandler authenticationSuccessHandler,
                                                                  AuthenticationFailureHandler authenticationFailureHandler, String... excludeUrl) {
        WebSocketRequestFilter filter = new WebSocketRequestFilter(path, authClass);
        filter.setPermissiveUrl(excludeUrl);
        filter.setSuccessHandler(authenticationSuccessHandler);
        filter.setFailureHandler(authenticationFailureHandler);
        return filter;
    }

    @Override
    public void configure(WebSecurity web) {
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new MyAuthenticationEntryPoint())
                .accessDeniedHandler(restAuthenticationAccessDeniedHandler)
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .csrf().disable()
                .formLogin().disable()
                .sessionManagement().disable()
                .cors()
                .and()
                .headers().addHeaderWriter(new StaticHeadersWriter(Arrays.asList(new Header("Access-control-Allow-Origin", "*"), new Header("Access-Control-Expose-Headers", "generateAuthorization"))))
                .and()
                .addFilterAfter(new OptionsRequestFilter(), CorsFilter.class)
                //配置token认证
                .apply(new JwtAuthorizeConfigurer<>())
                .addJwtAuthenticationFilter(configJwtAuthenticationFilter("/admin/**", UserJwtAuthenticationToken.class, null, tokenAuthenticationFailureHandler,
                        "/admin/login/account"))
//                .addJwtAuthenticationFilter(configJwtAuthenticationFilter("/message/**", UserJwtAuthenticationToken.class, null, tokenAuthenticationFailureHandler))
                .addJwtAuthenticationFilter(configJwtAuthenticationFilter("/app/**", UserJwtAuthenticationToken.class, null, tokenAuthenticationFailureHandler,
                        "/app/login/account", "/app/login/miniProgram", "/app/login/phone", "/app/login/authorize"))
                //配置登陆
                .and()
                .apply(new LoginConfigurer<>())
                .addFilter(configCodeLoginFilter("/app/login/phone", UserVerifyCodeAuthenticationToken.class, userCodeLoginSuccessHandler, codeLoginFailureHandler))
                .addFilter(configPasswordLoginFilter("/admin/login/account", UserUsernamePasswordAuthenticationToken.class, adminPasswordLoginSuccessHandler, loginFailureHandler))
                .addFilter(configMiniProgramLoginFilter("/app/login/miniProgram", UserMiniProgramAuthenticationToken.class, userMiniProgramLoginSuccessHandler, loginFailureHandler))
                .addFilter(configPasswordLoginFilter("/app/login/account", UserUsernamePasswordAuthenticationToken.class, userPasswordLoginSuccessHandler, loginFailureHandler))
                .addFilter(configWeChatLoginFilter("/app/login/authorize", UserWeChatAuthenticationToken.class, userWeChatLoginSuccessHandler, codeLoginFailureHandler))
                .addFilter(configAppleLoginFilter("/app/login/apple", UserAppleAuthenticationToken.class, userAppleLoginSuccessHandler, loginFailureHandler))
                .and()
        ;
    }
}
